<?php

namespace app\api;

use \think\Db;
use \think\Loader;
use \think\Request;
use \think\Controller;
use app\model\Conf as ConfModel;
use app\model\User as UserModel;
use app\model\Group as GroupModel;
use app\model\Node as NodeModel;
use app\model\Auth as AuthModel;
use app\model\Log as LogModel;
use app\model\Access as AccessModel;

class Api extends Controller
{
    protected $request;
    protected $module;
    protected $controller;
    protected $action;
    public $access_token;
    //用户信息
    protected $user = null;
    //用户组
    protected $group = null;
    //节点信息
    protected $node = null;
    //以下字段不允许POST修改
    protected $postSystem = ["version", "plat", "access_token"];
    //以下字段不允许POST修改
    protected $postBlackList = ["id", "status", "createtime", "updatetime"];
    //以下字段查询时不返回
    protected $selectFieldList = [];
    //搜索字段
    protected $searchFilter = [];
    //excel查询字段
    protected $excelField = [
        "id" => "编号",
        "createtime" => "创建时间",
        "updatetime" => "修改时间"
    ];
    //excel 表头
    protected $excelTitle = "数据导出表";
    //EXCEL 单元格字母
    protected $excelCells = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'AA', 'AB', 'AC', 'AD'];
    //默认页面分页条数
    protected $per_page = 20;
    //主键key
    protected $pk = '';
    //表名称
    protected $table = '';
    //主键value
    protected $pk_value = 0;
    protected $ConfModel;
    protected $UserModel;
    protected $GroupModel;
    protected $NodeModel;
    protected $AuthModel;
    protected $LogModel;
    protected $AccessModel;
    protected $plat = 'all';
    protected $version = 10000;
    public function __construct()
    {
        $this->ConfModel = new ConfModel();
        $this->UserModel = new UserModel();
        $this->GroupModel = new GroupModel();
        $this->NodeModel = new NodeModel();
        $this->AuthModel = new AuthModel();
        $this->LogModel = new LogModel();
        $this->AccessModel = new AccessModel();
        $configs = $this->ConfModel->select();
        $c = [];
        foreach ($configs as $config) {
            $c[$config['conf_key']] = $config['conf_value'];
        }
        config($c);
        $this->init();

        $this->pk = strtolower($this->controller) . "_id";
        $this->table = strtolower($this->controller);
        if (empty($this->searchFilter)) {
            $this->searchFilter = [$this->pk];
        }
    }

    /**
     * 捕获没有声明的方法
     *
     * @return void
     */
    public function _empty()
    {
        return jerr("没有找到请求的API资源", 404);
    }
    /**
     * 初始化请求数据
     */
    protected function init()
    {
        $this->request = Request::instance();
        $this->module = strtolower($this->request->module());
        $this->controller = strtolower($this->request->controller());
        $this->action = strtolower($this->request->action());
        //获取操作平台
        if (input("plat")) {
            $this->plat = input("plat");
        }
        //获取版本号
        if (input("version")) {
            $this->version = input("version");
        }
    }
    /**
     * AccessToken校验
     *
     * @return void
     */
    protected function auth()
    {
        //获取access_token
        if (input("?access_token")) {
            $this->access_token = input("access_token");
            $this->user = $this->UserModel->getUserByAccessToken($this->access_token);
            if (!$this->user) {
                return jerr("登录过期，请重新登录", 401);
            } else {
                if ($this->user['user_status'] == 1) {
                    return jerr("你的账户被禁用，登录失败", 401);
                } else {
                    return null;
                }
            }
        } else {
            return jerr("AccessToken为必要参数");
        }
    }

    /**
     * RBAC权限控制
     *
     * @return void
     */
    protected function rbac()
    {
        if (!$this->user['user_group']) {
            return jerr("用户没有所属的用户组", 403);
        }
        $where = [
            "group_id" => $this->user['user_group'],
        ];
        $this->group = $this->GroupModel->where($where)->find();
        if (!$this->group) {
            return jerr("用户组信息查询失败", 403);
        }
        if ($this->group['group_status'] == 1) {
            return jerr("你所在的用户组[" . $this->group['group_name'] . "]被禁用", 403);
        }
        $this->node = $this->NodeModel->getNodeByUrl(strtolower($this->module), strtolower($this->controller), strtolower($this->action));
        if (!$this->node) {
            return jerr("请勿访问没有声明的API节点！", 503);
        }
        if ($this->node['node_status'] == 1) {
            return jerr("你访问的节点[" . $this->node['node_title'] . "]被禁用", 503);
        }
        $log = [
            "log_user" => $this->user['user_id'],
            "log_node" => $this->node['node_id'],
            "log_createtime" => time(),
            "log_ip" => $this->request->ip(),
            "log_browser" => getBrowser(),
            "log_os" => getOs(),
            // "log_location"=>getLocation($this->request->ip()),
            "log_updatetime" => time(),
            "log_gets" => urlencode(json_encode(input("get."))),
            "log_posts" => urlencode(json_encode(input("post."))),
            "log_cookies" => urlencode(json_encode($_COOKIE))
        ];
        db("log")->insert($log);

        if ($this->group['group_id'] > 1) {
            //其他用户
            $where = [
                "auth_group" => $this->user["user_group"],
                "auth_node" => $this->node['node_id'],
            ];
            $auth = $this->AuthModel->auth($this->group['group_id'], $this->node['node_id']);
            if (!$auth) {
                return jerr("你没有权限访问[" . $this->node['node_title'] . "]这个接口", 403);
            }
        }
        return null;
    }

    /**
     * 默认修改方法 需要请覆写之类中的方法即可
     *
     * @return void
     */
    public function update()
    {
        //走AccessToken验证
        $error = $this->auth();
        if ($error) {
            return $error;
        }
        // //走Rbac验证
        $error = $this->rbac();
        if ($error) {
            return $error;
        }
        //业务逻辑开始 TODO
        if (!input($this->pk)) {
            return jerr("修改失败,缺少参数");
            exit;
        }
        $this->pk_value = input($this->pk);
        if (!isInteger($this->pk_value)) {
            return jerr("修改失败,参数错误");
            exit;
        }
        $map[$this->pk] = $this->pk_value;
        $item = db($this->table)->where($map)->find();
        if (empty($item)) {
            return jerr("没有这条记录，修改失败");
            exit;
        }
        $data = [];
        foreach (input("post.") as $k => $v) {
            if (in_array($k, $this->postBlackList) || in_array($k, $this->postSystem)) {
                continue;
            } else {
                $data[$k] = $v;
            }
        }
        $data[$this->table . "_updatetime"] = time();
        db($this->table)->where($this->pk, $this->pk_value)->update($data);
        return jok();
    }

    /**
     * 默认禁用方法 需要请覆写之类中的方法即可
     *
     * @return void
     */
    public function disable()
    {
        //走AccessToken验证
        $error = $this->auth();
        if ($error) {
            return $error;
        }
        //走Rbac验证
        $error = $this->rbac();
        if ($error) {
            return $error;
        }
        if (!input($this->pk)) {
            return jerr("禁用失败,缺少参数");
            exit;
        }
        $this->pk_value = input($this->pk);
        $map[$this->pk] = $this->pk_value;
        if (!isInteger($this->pk_value)) {
            $list = explode(',', $this->pk_value);
            $map[$this->pk] = ['in', $list];
        }
        db($this->table)->where($map)->update([
            $this->table . "_status" => 1,
            $this->table . "_updatetime" => time(),
        ]);
        return jok();
    }
    /**
     * 默认启用方法 需要请覆写之类中的方法即可
     *
     * @return void
     */
    public function enable()
    {
        //走AccessToken验证
        $error = $this->auth();
        if ($error) {
            return $error;
        }
        //走Rbac验证
        $error = $this->rbac();
        if ($error) {
            return $error;
        }
        //业务逻辑开始 TODO
        if (!input($this->pk)) {
            return jerr("启用失败,缺少参数");
            exit;
        }
        $this->pk_value = input($this->pk);
        $map[$this->pk] = $this->pk_value;
        if (!isInteger($this->pk_value)) {
            $list = explode(',', $this->pk_value);
            $map[$this->pk] = ['in', $list];
        }
        db($this->table)->where($map)->update([
            $this->table . "_status" => 0,
            $this->table . "_updatetime" => time(),
        ]);
        return jok();
    }
    /**
     * 默认删除方法 需要请覆写之类中的方法即可
     *
     * @return void
     */
    public function delete()
    {
        //走AccessToken验证
        $error = $this->auth();
        if ($error) {
            return $error;
        }
        //走Rbac验证
        $error = $this->rbac();
        if ($error) {
            return $error;
        }
        //业务逻辑开始 TODO
        if (!input($this->pk)) {
            return jerr("删除失败,缺少参数");
            exit;
        }
        $this->pk_value = input($this->pk);
        $map[$this->pk] = $this->pk_value;
        if (!isInteger($this->pk_value)) {
            $list = explode(',', $this->pk_value);
            $map[$this->pk] = ['in', $list];
        }
        db($this->table)->where($map)->delete();
        return jok();
    }

    /**
     * 默认添加方法 需要请覆写之类中的方法即可
     *
     * @return void
     */
    public function add()
    {
        //走AccessToken验证
        $error = $this->auth();
        if ($error) {
            return $error;
        }
        // //走Rbac验证
        $error = $this->rbac();
        if ($error) {
            return $error;
        }
        //业务逻辑开始 TODO
        $data = [];
        foreach (input("post.") as $k => $v) {
            if (in_array($k, $this->postBlackList) || in_array($k, $this->postSystem)) {
                continue;
            } else {
                $data[$k] = $v;
            }
        }
        $data[$this->table . "_updatetime"] = time();
        $data[$this->table . "_createtime"] = time();
        db($this->table)->insert($data);
        return jok();
    }

    /**
     * 获取所有列表方法 支持查询 排序 分页 需要请覆写之类中的方法即可
     *
     * @return void
     */
    public function lists()
    {
        //走AccessToken验证
        $error = $this->auth();
        if ($error) {
            return $error;
        }
        // //走Rbac验证
        $error = $this->rbac();
        if ($error) {
            return $error;
        }
        $map = [];
        $filter = input();
        if (input("per_page")) {
            $this->per_page = intval(input("per_page"));
        }
        if (input("keyword") && input("search")) {
            //存在查询条件
            if (array_key_exists(input("search"), $this->searchFilter)) {
                switch ($this->searchFilter[input("search")]) {
                    case "like":
                        $map[input("search")] = ["like", "%" . urldecode(input("keyword")) . "%"];
                        break;
                    case "=":
                        $map[input("search")] = urldecode(input("keyword"));
                        break;
                    default:
                }
            }
        } else {
            foreach ($filter as $k => $v) {
                if (array_key_exists($k, $this->searchFilter)) {
                    switch ($this->searchFilter[$k]) {
                        case "like":
                            $map[$k] = ["like", "%" . urldecode($v) . "%"];
                            break;
                        case "=":
                            $map[$k] = urldecode($v);
                            break;
                        default:
                    }
                }
            }
        }
        $order = $this->pk . " desc";
        if (input('order')) {
            $order = urldecode(input('order'));
        }
        $field = "*";
        if (count($this->selectFieldList) > 0) {
            $field = $this->pk;
            foreach ($this->selectFieldList as $selectField) {
                $field .= "," . $selectField;
            }
        }
        $datalist = db($this->table)->field($field)->where($map)->order($order)->paginate($this->per_page, false);
        return jok('success', $datalist);
    }
    /**
     * 获取我相关的数据列表 支持查询 分页 排序 需要请覆写之类中的方法即可
     *
     * @return void
     */
    public function mylist()
    {
        //走AccessToken验证
        $error = $this->auth();
        if ($error) {
            return $error;
        }
        // //走Rbac验证
        $error = $this->rbac();
        if ($error) {
            return $error;
        }
        $map = [];
        $filter = input();
        if (input("per_page")) {
            $this->per_page = intval(input("per_page"));
        }
        if (input("keyword") && input("search")) {
            //存在查询条件
            if (array_key_exists(input("search"), $this->searchFilter)) {
                switch ($this->searchFilter[input("search")]) {
                    case "like":
                        $map[input("search")] = ["like", "%" . urldecode(input("keyword")) . "%"];
                        break;
                    case "=":
                        $map[input("search")] = urldecode(input("keyword"));
                        break;
                    default:
                }
            }
        } else {
            foreach ($filter as $k => $v) {
                if (array_key_exists($k, $this->searchFilter)) {
                    switch ($this->searchFilter[$k]) {
                        case "like":
                            $map[$k] = ["like", "%" . urldecode($v) . "%"];
                            break;
                        case "=":
                            $map[$k] = urldecode($v);
                            break;
                        default:
                    }
                }
            }
        }
        $map[$this->table . "_user"] = $this->user['user_id'];
        $order = $this->pk . " desc";
        if (input('order')) {
            $order = urldecode(input('order'));
        }

        $field = "*";
        if (count($this->selectFieldList) > 0) {
            $field = $this->pk;
            foreach ($this->selectFieldList as $selectField) {
                $field .= "," . $selectField;
            }
        }
        $datalist = db($this->table)->field($field)->where($map)->order($order)->paginate($this->per_page, false);
        return jok('success', $datalist);
    }
    /**
     * 默认详情方法 需要请覆写之类中的方法即可
     *
     * @return void
     */
    public function detail()
    {
        //走AccessToken验证
        $error = $this->auth();
        if ($error) {
            return $error;
        }
        // //走Rbac验证
        $error = $this->rbac();
        if ($error) {
            return $error;
        }
        if (!input($this->pk)) {
            return jerr("参数错误 " . $this->pk . " 为必要参数");
        }
        $map = [
            $this->pk => input($this->pk),
            $this->table . "_status" => 0
        ];
        $field = "*";
        if (count($this->selectFieldList) > 0) {
            $field = $this->pk;
            foreach ($this->selectFieldList as $selectField) {
                $field .= "," . $selectField;
            }
        }
        $item = db($this->table)->field($field)->where($map)->find();
        if (empty($item)) {
            return jerr("没有查询到数据");
        }
        // 数据脱敏步骤 开始 示例
        //	    unset($item[$this->table."_password"]);
        //	    $item[$this->table."_truename"] = str_replace(mb_substr($item[$this->table."_truename"],1),"*",$item[$this->table."_truename"]);
        // 数据脱敏步骤 结束
        return jok('success', $item);
    }

    /**
     * 获取数据表模型方法
     *
     * @return void
     */
    public function model()
    {
        $sql = "select column_name as model_field , column_comment as model_desc,is_nullable as model_null,data_type as model_type from information_schema.columns where table_schema ='" . config('database.database') . "' and table_name = '" . config('database.prefix') . $this->table . "' ;";

        $ret = Db::query($sql);
        return jok('success', $ret);
    }

    /**
     * 查询指定表数量方法
     *
     * @return void
     */
    public function total()
    {
        $total = db($this->table)->field($this->pk)->count();
        return jok("success", ['total' => $total]);
    }
    public function excel()
    {
        //走AccessToken验证
        $error = $this->auth();
        if ($error) {
            return $error;
        }
        // //走Rbac验证
        $error = $this->rbac();
        if ($error) {
            return $error;
        }
        $map = [];
        $filter = input();
        if (input("keyword") && input("search")) {
            //存在查询条件
            if (array_key_exists(input("search"), $this->searchFilter)) {
                switch ($this->searchFilter[input("search")]) {
                    case "like":
                        $map[input("search")] = ["like", "%" . urldecode(input("keyword")) . "%"];
                        break;
                    case "=":
                        $map[input("search")] = urldecode(input("keyword"));
                        break;
                    default:
                }
            }
        } else {
            foreach ($filter as $k => $v) {
                if (array_key_exists($k, $this->searchFilter)) {
                    switch ($this->searchFilter[$k]) {
                        case "like":
                            $map[$k] = ["like", "%" . urldecode($v) . "%"];
                            break;
                        case "=":
                            $map[$k] = urldecode($v);
                            break;
                        default:
                    }
                }
            }
        }
        $order = $this->pk . " desc";
        if (input('order')) {
            $order = urldecode(input('order'));
        }

        $field = "";
        $excelField = [];
        foreach ($this->excelField as $k => $v) {
            if ($k == "*") {
                continue;
            } else {
                array_push($excelField, [
                    $k, $v
                ]);
                if ($field) {
                    $field .= "," . $this->table . "_" . $k;
                } else {
                    $field .= $this->table . "_" . $k;
                }
            }
        }
        $datalist = db($this->table)->field($field)->where($map)->order($order)->select();
        // print_r($datalist[0]);
        Loader::import('PHPExcel.PHPExcel'); //手动引入PHPExcel.php
        Loader::import('PHPExcel.PHPExcel.IOFactory.PHPExcel_IOFactory'); //引入IOFactory.php 文件里面的PHPExcel_IOFactory这个类
        $PHPExcel = new \PHPExcel(); //实例化

        $PHPExcel
            ->getProperties()  //获得文件属性对象，给下文提供设置资源  
            ->setCreator("FuckAdmin")                 //设置文件的创建者  
            ->setLastModifiedBy("FuckAdmin")          //设置最后修改者  
            ->setDescription("Export by FuckAdmin"); //设置备注  

        $PHPSheet = $PHPExcel->getActiveSheet();
        $PHPSheet->setTitle($this->excelTitle); //给当前活动sheet设置名称

        $PHPSheet->mergeCells('A1:' . $this->excelCells[count($excelField) - 1] . "1");
        $PHPSheet->setCellValue('A1', $this->excelTitle);
        $PHPSheet->getRowDimension(1)->setRowHeight(40);
        $PHPSheet->getStyle('A1')->getFont()->setSize(18)->setBold(true); //字体大小

        $PHPSheet->getStyle('A1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);    //水平方向上对齐  
        $PHPSheet->getStyle('A1')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);       //垂直方向上中间居中  
        $PHPSheet->getStyle('A1')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);       //垂直方向上中间居中  

        if (count($excelField) > count($this->excelCells)) {
            echo 'Error and you need check Excel Cells Keys...';
            die;
        }
        for ($column = 0; $column < count($excelField); $column++) {
            $PHPSheet->setCellValue($this->excelCells[$column] . "2", $excelField[$column][1]);
            for ($line = 0; $line < count($datalist); $line++) {
                switch ($excelField[$column][0]) {
                    case 'createtime':
                    case 'updatetime':
                        $PHPSheet->getColumnDimension($this->excelCells[$column])->setWidth(20);
                        $PHPSheet->setCellValue($this->excelCells[$column] . ($line + 3), date('Y-m-d H:i:s', " " . $datalist[$line][$this->table . "_" . $excelField[$column][0]]));
                        break;
                    default:
                        $PHPSheet->setCellValue($this->excelCells[$column] . ($line + 3), $datalist[$line][$this->table . "_" . $excelField[$column][0]]);
                }
            }
        }

        //***********************画出单元格边框*****************************
        $styleArray = array(
            'borders' => array(
                'inside' => array(
                    'style' => \PHPExcel_Style_Border::BORDER_THIN, //细边框
                    //'color' => array('argb' => 'FFFF0000'),
                ),
                'outline' => array(
                    'style' => \PHPExcel_Style_Border::BORDER_THICK, //边框是粗的
                    //'color' => array('argb' => 'FFFF0000'),
                ),
            ),
        );
        $PHPSheet->getStyle('A2:' . $this->excelCells[count($excelField) - 1] . (count(
            $datalist
        ) + 2))->applyFromArray($styleArray);
        //***********************画出单元格边框结束*****************************

        //设置全部居中对齐
        $PHPSheet->getStyle('A1:' . $this->excelCells[count($excelField) - 1] . (count(
            $datalist
        ) + 2))->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER)->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER)->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);
        //设置全部字体
        $PHPSheet->getStyle('A1:' . $this->excelCells[count($excelField) - 1] . (count(
            $datalist
        ) + 2))->getFont()->setName('微软雅黑');
        //设置格式为文本
        // $PHPSheet->getStyle('A1:' . $this->excelCells[count($excelField) - 1] . (count(
        //     $datalist
        // ) + 2))->getNumberFormat()
        //     ->setFormatCode(\PHPExcel_Style_NumberFormat::FORMAT_TEXT);
        $PHPWriter = \PHPExcel_IOFactory::createWriter($PHPExcel, "Excel2007"); //创建生成的格式
        header('Content-Disposition: attachment;filename="' . $this->excelTitle . "_" . date('Y-m-d_H:i:s') . '.xlsx"'); //下载下来的表格名
        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        $PHPWriter->save("php://output"); //表示在$path路径下面生成demo.xlsx文件
    }
}
